package com.bite.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.security.exception.ServiceException;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamQuestion;
import com.bite.system.domain.exam.dto.ExamAddDTO;
import com.bite.system.domain.exam.dto.ExamEditDTO;
import com.bite.system.domain.exam.dto.ExamQueryDTO;
import com.bite.system.domain.exam.dto.ExamQuestionAddDTO;
import com.bite.system.domain.exam.vo.ExamDetailVO;
import com.bite.system.domain.exam.vo.ExamVO;
import com.bite.system.domain.question.Question;
import com.bite.system.domain.question.vo.QuestionVO;
import com.bite.system.manager.ExamCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamQuestionMapper;
import com.bite.system.mapper.question.QuestionMapper;
import com.bite.system.service.exam.IExamService;
import com.github.pagehelper.PageHelper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {

    @Resource
    private ExamMapper examMapper;

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private ExamQuestionMapper examQuestionMapper;

    @Resource
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());

        return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }



    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        //获取前端发来的题目集合
        //竞赛题目添加时，查看当前竞赛是否开始
        checkExam(exam);
        Set<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            //一条题目也不添加
            return true ;
        }
        //查询数据库中的题目
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()) {
            //如果查出的题目id为空，或者查出的数据比前端给的少，说明数据中有资源不存在
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }


       return saveExamQuestion(exam,questionIdSet);
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        //1.获取基本信息
        ExamDetailVO examDetailVO = new ExamDetailVO();
        Exam exam = getExam(examId);
        //2.exam赋值给vo
        BeanUtil.copyProperties(exam,examDetailVO);
        //3.进行查询,在tb_exam_question
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtil.isEmpty(examQuestionList)) {
            //直接返回,因为没有题目id,此时只有基本信息
            return examDetailVO;
        }
        //4.包含题目信息,获取题目信息
        //把竞赛id收集到一个list中
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        //5.查询竞赛id的详细信息 select * from tb_question where question_id in (1,2,3)
        // Question::getQuestionId  是in 之前的属性,后面的 questionIdList 是in之后的参数id
        List<Question> questionList= questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
        //6.questionList 转换为 questionVOList
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList,QuestionVO.class);
        //7.vo赋值给examDetailVO,返回给前端
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if(Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        //竞赛
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        exam.setTitle(examEditDTO.getTitle());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        //1.查询竞赛是否存在
        Exam exam = getExam(examId);
        //2.检查竞赛是否开始,在当前时间之前是开始
        checkExam(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));

    }

    @Override
    public int delete(Long examId) {
        //1.根据竞赛id,判断竞赛是否存在
        Exam exam = getExam(examId);
        if(Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        //竞赛是否存在
        Exam exam = getExam(examId);
        //1.查询题目有多少个 select count(0) from tb_exam_question where examId = examId
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }

        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if (count == null ||count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }

        //2.竞赛存在并且是包含题目的
        exam.setStatus(Constants.TRUE);

        //3.往 redis 中进行发布过竞赛的存储  e:t:l(列表缓存)  e:d:examId(基本信息的缓存)
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {

        //1.竞赛是否存在
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        //2.竞赛是否开始
        checkExam(exam);
        //3.设置状态
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private static void checkExam(Exam exam) {
        if(exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId) {
        //竞赛标题重复判断  开始时间和结束时间
        //用户在进行更改的时候,标题不能和已有的进行重复,但是可以和自己重复
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                                .ne(examId != null, Exam::getExamId,examId)
                        );
        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if (examSaveDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);  //竞赛开始时间不能早于当前时间
        }
        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    private boolean saveExamQuestion(Exam exam,Set<Long> questionIdSet) {
        int num = 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for(Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(exam.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
            //ExamQuestion是插入到集合中的单个题目数据
        }
       return saveBatch(examQuestionList);
    }

    private Exam getExam(Long examId) {
        //根据竞赛id查找竞赛相关的基本信息
        Exam exam = examMapper.selectById(examId);
        if(exam == null) {
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }
}
